﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Input;
using Peter.Common;
using Peter.Common.Dialog;
using Peter.Common.Interfaces;
using Peter.Sdw.Data;
using Peter.Sdw.Logic.Dialogs;

namespace Peter.Sdw.Logic
{
   /// <summary>
   /// Model for the term list.
   /// </summary>
   public class TermListModel : ViewModelBase
   {
      private int m_Start;
      private bool m_ShowAdd;
      private bool m_IsLoading;
      private string m_TermSearch;
      private TermModel m_SelectedTerm;
      private RelayCommand m_CmdRefresh;
      private RelayCommand m_CmdAddTerm;
      private RelayCommand m_CmdOpenTerm;
      private RelayCommand m_CmdNavResults;
      private TermAddModel m_AddTermDialog;
      private readonly ObservableCollection<TermModel> m_Terms;

      /// <summary>
      /// Initializes a new term list model.
      /// </summary>
      public TermListModel ()
      {
         this.m_ShowAdd = true;
         this.m_Terms = new ObservableCollection <TermModel> ();
      }

      /// <summary>
      /// Gets the list of terms to display.
      /// </summary>
      public ObservableCollection<TermModel> Terms { get { return this.m_Terms; } }

      /// <summary>
      /// Gets or Sets the search term text
      /// </summary>
      public string TermSearch
      {
         get { return this.m_TermSearch; }
         set
         {
            this.m_TermSearch = value;
            this.RaisePropertyChanged ("TermSearch");
            this.GetTerms();
         }
      }

      /// <summary>
      /// Gets or Sets the selected term
      /// </summary>
      public TermModel SelectedTerm
      {
         get { return this.m_SelectedTerm; }
         set
         {
            this.m_SelectedTerm = value;
            this.RaisePropertyChanged ("SelectedTerm");
         }
      }

      /// <summary>
      /// Gets or Sets if we are loading data.
      /// </summary>
      public bool IsLoading
      {
         get { return this.m_IsLoading; }
         set
         {
            this.m_IsLoading = value;
            this.RaisePropertyChanged ("IsLoading");
         }
      }

      /// <summary>
      /// Gets or Sets if the add button should be shown or not.
      /// </summary>
      public bool ShowAdd
      {
         get { return this.m_ShowAdd; }
         set
         {
            this.m_ShowAdd = value;
            this.RaisePropertyChanged("ShowAdd");
         }
      }

      /// <summary>
      /// Gets or Sets the peter instance.
      /// </summary>
      public IPeter Peter { get; set; }

      /// <summary>
      /// Gets the command to add a new term.
      /// </summary>
      public ICommand AddTermCommand
      {
         get { return this.m_CmdAddTerm ?? (this.m_CmdAddTerm = new RelayCommand (this.OnAddTerm, this.CanAddTerm)); }
      }

      /// <summary>
      /// Gets the command used to refresh the list.
      /// </summary>
      public ICommand RefreshCommand
      {
         get { return this.m_CmdRefresh ?? (this.m_CmdRefresh = new RelayCommand (this.OnRefresh, this.CanAddTerm)); }
      }

      /// <summary>
      /// Gets the command to navigate the results.
      /// </summary>
      public ICommand NavigateListCommand
      {
         get { return this.m_CmdNavResults ?? (this.m_CmdNavResults = new RelayCommand (this.OnNavigateResults, this.CanNavigateResults)); }
      }

      /// <summary>
      /// Gets the command used to execute.
      /// </summary>
      public ICommand OpenTermCommand
      {
         get { return this.m_CmdOpenTerm ?? (this.m_CmdOpenTerm = new RelayCommand(this.OnOpenTerm)); }
      }

      /// <summary>
      /// Executes the selected item.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnOpenTerm (object obj)
      {
         if (this.ShowAdd) {
            if (this.SelectedTerm == null) return;
            this.SelectedTerm.IsActive = true;
            this.SelectedTerm.IsSelected = true;
            this.Peter.AddDocument (this.SelectedTerm);
            this.SelectedTerm.GetItems();
         }
      }

      /// <summary>
      /// Checks to see if the list can be navigated
      /// </summary>
      /// <param name="obj">Command parameter</param>
      /// <returns>True if the command can execute, otherwise false.</returns>
      private bool CanNavigateResults (object obj)
      {
         var dir = obj.ToString ();
         if (dir == "prev")
            return this.m_Start > 0;
         return this.Terms.Count >= 25;
      }

      /// <summary>
      /// Navigates the list of results.
      /// </summary>
      /// <param name="obj">Command parameter</param>
      private void OnNavigateResults (object obj)
      {
         var dir = obj.ToString ();
         if (dir == "prev")
            this.m_Start -= 25;
         else
            this.m_Start += 25;
         this.GetTerms();
      }

      /// <summary>
      /// Checks to see if a new term can be added.
      /// </summary>
      /// <param name="obj">Command parameter</param>
      /// <returns>True if the command can execute, otherwise false.</returns>
      private bool CanAddTerm (object obj)
      {
         return this.ShowAdd && WebQueue.Instance.IsAuthenticated;
      }

      /// <summary>
      /// Refreshes the list of terms.
      /// </summary>
      /// <param name="obj">Command parameter.</param>
      private void OnRefresh (object obj)
      {
         this.GetTerms ();
      }

      /// <summary>
      /// Shows the add term dialog.
      /// </summary>
      /// <param name="obj">Command parameter</param>
      private void OnAddTerm (object obj)
      {
         if (this.m_AddTermDialog == null)
            this.m_AddTermDialog = new TermAddModel ();
         this.m_AddTermDialog.Closing += this.OnAddTermClosing;
         this.Peter.ShowDialog (this.m_AddTermDialog);
      }

      /// <summary>
      /// Occurs when the add term dialog is closing.
      /// </summary>
      /// <param name="dialog">The dialog closing.</param>
      private void OnAddTermClosing (DialogBase dialog)
      {
         dialog.Closing -= this.OnAddTermClosing;
         if (dialog.DialogCloseAction != DialogCloseAction.Ok) return;
         var model = dialog as TermAddModel;
         if (model != null)
         {
            var parameters = new NameValueCollection
               {
                  { Constants.REQUEST_TOKEN, WebQueue.Instance.RequestToken},
                  { "name", this.m_AddTermDialog.TermName }
               };
            WebQueue.Instance.WebPostComplete += this.OnPostCompleted;
            WebQueue.Instance.Post (Constants.URL_CREATE_TERM, parameters);
         }
      }

      /// <summary>
      /// Loads the terms.
      /// </summary>
      public void GetTerms ()
      {
         this.IsLoading = true;
         WebQueue.Instance.WebGetComplete += this.OnGetCompleted;
         WebQueue.Instance.Get (string.Format(Constants.URL_TERM_LIST, this.m_Start, this.TermSearch));
      }

      /// <summary>
      /// Occurs when a web get request is completed.
      /// </summary>
      /// <param name="sender">WebQueue.</param>
      /// <param name="e">WebRequestEventArgs</param>
      private void OnGetCompleted (object sender, WebRequestEventArgs e)
      {
         try
         {
            if (e.Url == string.Format (Constants.URL_TERM_LIST, this.m_Start, this.TermSearch))
            {
               WebQueue.Instance.WebGetComplete -= this.OnGetCompleted;
               if (e.Response.status == Constants.SUCCESS)
               {
                  this.Terms.Clear ();
                  foreach (var term in e.Response.terms)
                  {
                     this.Terms.Add (new TermModel
                     {
                        Id = term.id,
                        Name = term.name,
                        //Modified = DateTime.Parse(term.modified)
                     });
                  }
               }
               this.IsLoading = false;
            }
         }
         catch (Exception ex)
         {
            Console.Error.WriteLine (ex.Message);
         }
         CommandManager.InvalidateRequerySuggested ();
      }

      /// <summary>
      /// Occurs when a web post request is completed.
      /// </summary>
      /// <param name="sender">WebQueue.</param>
      /// <param name="e">WebRequestEventArgs</param>
      private void OnPostCompleted (object sender, WebRequestEventArgs e)
      {
         try
         {
            if (e.Url == Constants.URL_CREATE_TERM)
            {
               WebQueue.Instance.WebPostComplete -= this.OnPostCompleted;
               if (e.Response.status == Constants.SUCCESS)
               {
                  dynamic term = e.Response;
                  this.Terms.Add (new TermModel { Id = term.id, Name = term.name });
                  this.m_AddTermDialog.TermName = string.Empty;
               }
            }
         }
         catch (Exception ex)
         {
            Console.Error.WriteLine (ex.Message);
         }
      }
   }
}
